/**
 * 
 */
package de.gee.erep.server.xml;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

import com.google.gwt.core.client.GWT;

import de.gee.erep.shared.entities.citizen.Citizen;
import de.gee.erep.shared.entities.citizen.CitizenDetail;
import de.gee.erep.shared.entities.citizen.CitizenDetailId;
import de.gee.erep.shared.entities.citizen.details.Card;
import de.gee.erep.shared.entities.citizen.details.CitizenShip;
import de.gee.erep.shared.entities.citizen.details.Medal;
import de.gee.erep.shared.entities.citizen.details.Medals;
import de.gee.erep.shared.entities.citizen.details.MilitarySkill;
import de.gee.erep.shared.entities.citizen.details.Party;
import de.gee.erep.shared.entities.citizen.details.Residence;

/**
 * @author Matthew Gee created: 14.03.2011
 */
public class CitizenParser extends ErepParser<Citizen> {
	public static Map<CitizenDetailId, CitizenDetail> map = new HashMap<CitizenDetailId, CitizenDetail>();
	public int idCount = 0;
	private static final String ROOT = "citizens";
	private static final String CITIZEN = "citizen";
	public static final String[][] FIELDS = { Card.fieldnames,
			Party.fieldnames, Medals.fieldnames, MilitarySkill.fieldnames };
	private String filename;

	/**
	 * @param filename
	 *            f
	 */
	public CitizenParser(final String filename) {
		super();
		this.filename = filename;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.gee.erep.server.xml.ErepParser#parse()
	 */
	@Override
	public final void parse() throws XMLStreamException {
		if (filename == null) {
			try {
				parseUpdate();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return;
		}
		XMLInputFactory input = XMLInputFactory.newInstance();
		BufferedInputStream stream = null;
		try {
			stream = new BufferedInputStream(new FileInputStream(this.filename));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			GWT.log("error while parsing file: " + filename);
			return;
		}
		XMLEventReader evRd = input.createXMLEventReader(stream);
		while (evRd.hasNext()) {
			XMLEvent ev = evRd.nextEvent();
			if (ev.isStartElement()) {
				String elementName = ev.asStartElement().getName()
						.getLocalPart();
				if ((elementName.equals(ROOT))) {
					continue;
				}
				if (elementName.equals(CITIZEN)) {
					parseCitizen(evRd);
				}
			}
		}
		System.out.println("ready");
	}

	/**
	 * -herunterladen.
	 * -entpacken
	 * -normal weiter machen
	 * @throws IOException
	 * @throws MalformedURLException
	 * @throws XMLStreamException
	 * 
	 */
	private void parseUpdate() throws MalformedURLException, IOException,
			XMLStreamException {
		XMLInputFactory input = XMLInputFactory.newInstance();
		BufferedInputStream stream = new BufferedInputStream(new URL(
				"http://api.erepublik.com/v2/feeds/countries").openStream());
		XMLEventReader evRd = input.createXMLEventReader(stream);
		while (evRd.hasNext()) {
			XMLEvent ev = evRd.nextEvent();
			if (ev.isStartElement()) {
				String elementName = ev.asStartElement().getName()
						.getLocalPart();
				if ((elementName.equals(ROOT))) {
					continue;
				}
				if (elementName.equals(CITIZEN)) {
					parseCitizen(evRd);
				}
			}
		}
	}

	/**
	 * @param evRd
	 *            reader
	 * @throws XMLStreamException
	 */
	private void parseCitizen(final XMLEventReader evRd)
			throws XMLStreamException {
		Citizen c = new Citizen();
		Card card = new Card(new CitizenDetailId(String.valueOf(idCount++)));
		String elementName = "";
		while (evRd.hasNext()) {
			XMLEvent ev = evRd.nextEvent();
			if (ev.isEndElement()
					&& ev.asEndElement().getName().getLocalPart()
							.equals(CITIZEN)) {
				c.add(card.getId());
				map.put(card.getId(), card);
				getResult().add(c);
				c = new Citizen();
				card = new Card(new CitizenDetailId(String.valueOf(idCount++)));
				continue;
			}
			if (ev.isStartElement()) {
				elementName = ev.asStartElement().getName().getLocalPart();
				if (elementName.equals("party")) {
					c.add(parseParty(evRd).getId());
				} else if (elementName.equals("residence")) {
					c.add(parseResidenceOrCitizenship(evRd, true).getId());
				} else if (elementName.equals("citizenship")) {
					c.add(parseResidenceOrCitizenship(evRd, false).getId());
				} else if (elementName.equals("medals")) {
					c.add(parseMedals(evRd, elementName).getId());
				} else if (elementName.equals("military-skill")) {
					c.add(parseMilitary(evRd, elementName).getId());
				} else {
					if (oneOf(Card.fieldnames, elementName)) {
						if (elementName.equals("employer")) {
							evRd.nextTag();
							XMLEvent event = evRd.nextEvent();
							if (event.asCharacters().getData().trim().length() == 0) {
								card.set("employerId", "0");
								continue;
							}
							card.set("employerId", event.asCharacters()
									.getData());
						} else {
							try {
								card.set(elementName, evRd.nextEvent()
										.asCharacters().getData());
							} catch (ClassCastException e) {
								card.set(elementName, "0");
							}
						}
					} else if (oneOf(Citizen.fieldnames, elementName)) {
						c.set(elementName, evRd.nextEvent().asCharacters()
								.getData());
					}
				}
			}
		}
	}

	/**
	 * @param el
	 *            e
	 * @param field
	 *            f
	 * @return boolean
	 */
	private boolean oneOf(final String[] field, final String el) {
		for (String string : field) {
			if (string.equals(el)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @param evRd
	 * @param elementName
	 * @return
	 * @throws XMLStreamException
	 */
	private CitizenDetail parseMilitary(XMLEventReader r, String element)
			throws XMLStreamException {
		CitizenDetailId id = new CitizenDetailId(String.valueOf(idCount++));
		CitizenDetail result = new MilitarySkill(id);
		String el = "";
		while (r.hasNext()) {
			XMLEvent event = r.nextEvent();
			if (event.isEndElement()
					&& event.asEndElement().getName().getLocalPart()
							.equals(element)) {
				break;
			} else if (event.isStartElement()) {
				el = event.asStartElement().getName().getLocalPart();
				((MilitarySkill) result).set(el, characts(r));
				event = r.nextTag();
				((MilitarySkill) result).set(event.asStartElement().getName()
						.getLocalPart(), characts(r));
				event = r.nextTag();
				((MilitarySkill) result).set(event.asStartElement().getName()
						.getLocalPart(), characts(r));

			}
		}
		map.put(result.getId(), result);
		return result;
	}

	/**
	 * @param r
	 * @return
	 * @throws XMLStreamException
	 */
	private String characts(XMLEventReader r) throws XMLStreamException {
		String data = "";
		try {
			data = r.nextEvent().asCharacters().getData();
		} catch (ClassCastException e) {
			return data;
		}
		r.nextTag();
		return data;
	}

	/**
	 * @param element
	 * @param evRd
	 * @return
	 * @throws XMLStreamException
	 */
	private CitizenDetail parseMedals(XMLEventReader r, String element)
			throws XMLStreamException {
		CitizenDetailId id = new CitizenDetailId(String.valueOf(idCount++));
		CitizenDetail result = new Medals(id);
		// Medal m1 = m.new Medal("t", 20);
		String el = "";
		while (r.hasNext()) {
			XMLEvent event = r.nextEvent();
			if (event.isEndElement()
					&& event.asEndElement().getName().getLocalPart()
							.equals(element)) {
				break;
			} else if (event.isStartElement()) {
				el = event.asStartElement().getName().getLocalPart();
				if (el.equals("medal")) {
					parseMedal(r, el, (Medals) result);
				}
			}
		}
		map.put(result.getId(), result);
		return result;
	}

	/**
	 * @param r
	 * @param el
	 * @param result
	 * @throws XMLStreamException
	 */
	private void parseMedal(XMLEventReader r, String el, Medals result)
			throws XMLStreamException {
		while (r.hasNext()) {
			XMLEvent n = r.nextEvent();
			if (n.isEndElement()
					&& n.asEndElement().getName().getLocalPart().equals(el)) {
				return;
			} else if (n.isStartElement()) {
				String name = n.asStartElement().getName().getLocalPart();
				if (name.equals(el)) {
					parseMedal(r, el, result);
				} else {
					XMLEvent next = r.nextEvent();
					String type = next.asCharacters().getData();
					r.nextTag();
					r.nextTag();
					String amount = characts(r);
					result.addMedal(new Medal(type, Integer
							.valueOf(amount)));
				}
			}
		}
	}

	/**
	 * @param evRd
	 * @return
	 * @throws XMLStreamException
	 */
	private CitizenDetail parseResidenceOrCitizenship(XMLEventReader evRd,
			boolean flag) throws XMLStreamException {
		CitizenDetailId detailId = new CitizenDetailId(
				String.valueOf(this.idCount++));
		CitizenDetail d;
		if (flag) {
			d = new Residence(detailId);
		} else {
			d = new CitizenShip(detailId);
		}
		String element = "";

		while (evRd.hasNext()) {
			XMLEvent e = evRd.nextEvent();
			if (e.isEndElement()) {
				if (e.asEndElement().getName().getLocalPart()
						.equals(flag ? "residence" : "citizenship")) {
					break;
				}
				continue;
			}
			if (e.isStartElement()) {
				element = e.asStartElement().getName().getLocalPart();
				if (flag) {
					m(evRd, d, element, flag);
				} else {
					m(evRd, d, element, flag);
				}
			}
		}
		map.put(d.getId(), d);
		return d;
	}

	/**
	 * @param r
	 * @param d
	 * @param element
	 * @param flag
	 * @throws XMLStreamException
	 */
	private void m(XMLEventReader r, CitizenDetail d, String element,
			boolean flag) throws XMLStreamException {
		while (r.hasNext()) {
			XMLEvent next = r.nextEvent();
			if (next.isEndElement()
					&& next.asEndElement().getName().getLocalPart()
							.equals(element)) {
				return;
			} else if (next.isStartElement()) {
				String name = next.asStartElement().getName().getLocalPart();
				if (flag) {
					((Residence) d).set(element, characts(r));
				} else {
					((CitizenShip) d).set(element, characts(r));
				}
			}
		}
	}

	/**
	 * @param evRd
	 * @return
	 * @throws XMLStreamException
	 */
	private CitizenDetail parseParty(XMLEventReader evRd)
			throws XMLStreamException {
		CitizenDetailId detailId = new CitizenDetailId(
				String.valueOf(this.idCount++));
		Party p = new Party();
		String element = "";
		while (evRd.hasNext()) {
			XMLEvent nextEvent = evRd.nextEvent();
			if (nextEvent.isEndElement()) {
				if (nextEvent.asEndElement().getName().getLocalPart()
						.equals("party")) {
					break;
				}
				continue;
			}
			if (nextEvent.isStartElement()) {
				element = nextEvent.asStartElement().getName().getLocalPart();
				for (String field : Party.fieldnames) {
					if (field.equals(element)) {
						p.set(field, characts(evRd));
					}
				}
			}
		}
		p.setId(detailId);
		CitizenDetail detail = p;
		map.put(p.getId(), p);
		return detail;
	}

	/**
	 * @return
	 */
	public Map<CitizenDetailId,CitizenDetail> getDetailMap() {
		return map;
	}
}
